package com.own.business.wallet.business.util;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.own.business.pay.common.constant.ConstantPay;
import com.own.business.pay.common.em.PayEnum;
import com.own.business.pay.core.method.TradeSuccessHandlerService;
import com.own.business.pay.core.service.BillHistoryService;
import com.own.business.wallet.business.entity.PayResultEntity;
import com.own.business.wallet.business.entity.WalletPayOrderForm;
import com.own.business.wallet.business.entity.WalletRefundOrderForm;
import com.own.business.wallet.business.service.WalletHistoryService;
import com.own.business.wallet.business.service.WalletService;
import com.own.business.wallet.business.util.lock.OrderLockUtil;
import com.own.business.wallet.business.util.lock.WalletLockUtil;
import com.own.business.wallet.common.exception.WalletException;
import com.own.business.wallet.common.property.WalletPayProperty;
import com.own.business.wallet.entity.po.WalletHistory;
import com.own.component.common.util.executor.ExecutorUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * WalletBusinessUtil
 *
 * @author chenxueli
 * @date 2023-08-28 15:59:03
 */
@Slf4j
@Component
public class WalletBusinessUtil {

    private static final Map<Integer, String> PAY_ERROR_MESSAGE = new HashMap<>() {{
        put(ConstantPay.Status.COMPLETE_SUCCESS, "支付失败，订单已经支付了");
        put(ConstantPay.Status.COMPLETE_CLOSE, "支付失败，订单已经被关闭了");
        put(ConstantPay.Status.COMPLETE_TIMEOUT, "支付失败，订单超时关闭了");
    }};


    @Resource
    private WalletPayProperty walletPayProperty;

    @Resource
    private WalletService walletService;
    @Resource
    private WalletHistoryService walletHistoryService;
    @Resource
    private BillHistoryService billHistoryService;

    @Resource
    private WalletLockUtil walletLockUtil;
    @Resource
    private OrderLockUtil orderLockUtil;
    @Resource
    private ExecutorUtil executorUtil;
    @Resource
    private WalletOrderNoGenerateUtil walletOrderNoGenerateUtil;

    @Resource
    private List<TradeSuccessHandlerService> tradeSuccessHandlerServiceList;

    /**
     * 创建消费订单（支付订单）
     *
     * @param userId 用户id
     * @param form   订单表单
     * @return 订单信息
     */
    @Transactional(rollbackFor = Exception.class)
    public String createPayOrder(Long userId, WalletPayOrderForm form) {
        var history = new WalletHistory();
        history.setFkUserId(userId);
        // 获取当前用户的钱包数据
        var wallet = walletService.getByUserId(userId);
        history.setFkWalletId(wallet.getId());
        // 生成订单号信息
        var orderNo = walletOrderNoGenerateUtil.get(ConstantPay.Type.PAY);
        history.setTradeNo(orderNo);
        history.setOutTradeNo(form.getOutTradeNo());
        history.setBody(form.getBody());
        history.setSubject(form.getSubject());
        history.setType(ConstantPay.Type.PAY);
        history.setTotalAmount(form.getTotalAmount());
        history.setStatus(ConstantPay.Status.NEW_BUILT);
        history.setExpireTime(form.getExpireTime());
        walletHistoryService.add(history);
        // 返回订单号信息
        return WalletOrderNoGenerateUtil.handler(orderNo);
    }

    /**
     * 创建收入订单（一般用作充值）
     *
     * @param userId 用户id
     * @param form   订单表单
     */
    @Transactional(rollbackFor = Exception.class)
    public void executeIncomeOrder(Long userId, WalletPayOrderForm form) {
        orderLockUtil.lock(form.getOutTradeNo(), () -> {
            var history = new WalletHistory();
            history.setFkUserId(userId);
            // 获取当前用户的钱包数据
            var wallet = walletService.getByUserId(userId);
            history.setFkWalletId(wallet.getId());
            // 生成订单号信息
            var orderNo = walletOrderNoGenerateUtil.get(ConstantPay.Type.INCOME);
            history.setFkUserId(userId);
            history.setFkWalletId(wallet.getId());
            history.setTradeNo(orderNo);
            history.setOutTradeNo(form.getOutTradeNo());
            history.setBody(form.getBody());
            history.setSubject(form.getSubject());
            history.setType(ConstantPay.Type.INCOME);
            history.setTotalAmount(form.getTotalAmount());
            history.setStatus(ConstantPay.Status.COMPLETE_SUCCESS);
            history.setExpireTime(LocalDateTime.now().plusYears(7));
            walletHistoryService.add(history);
            // 直接调用余额增加的业务
            walletService.increase(wallet.getId(), form.getTotalAmount());
            // 调用回调信息
            callback(walletPayProperty.getNotifyUrl(), history);
        });
    }

    /**
     * 创建退款订单
     *
     * @param userId 用户id
     * @param form   订单表单
     */
    @Transactional(rollbackFor = Exception.class)
    public void executeRefundOrder(Long userId, WalletRefundOrderForm form) {
        form.check();
        orderLockUtil.lock(form.getOutTradeNo(), () -> {
            // 查询之前的订单流水信息
            var historyList = walletHistoryService.listByOutTradeNo(form.getOutTradeNo());
            // 验证订单信息
            var payAmount = 0;
            var refundAmount = 0;
            WalletHistory tradeHistory = null;
            for (var history : historyList) {
                if (history.getType() == 1) {
                    refundAmount += history.getTotalAmount();
                }
                if (history.getType() == 2) {
                    payAmount += history.getTotalAmount();
                    if (tradeHistory != null) {
                        throw new WalletException("订单异常，请联系管理员");
                    }
                    tradeHistory = history;
                }
                if (history.getType() == 3) {
                    refundAmount += history.getTotalAmount();
                }
            }
            if (tradeHistory == null) {
                throw new WalletException("没有找到支付订单");
            }
            if (refundAmount == payAmount) {
                throw new WalletException("订单已经全部退款");
            }
            if ((refundAmount + form.getTotalAmount()) > payAmount) {
                throw new WalletException("退款金额不能大于支付金额");
            }
            // 生成订单号信息
            var refundNo = walletOrderNoGenerateUtil.get(ConstantPay.Type.REFUND);
            // 创建订单信息
            var history = new WalletHistory();
            history.setFkUserId(userId);
            history.setFkWalletId(tradeHistory.getFkWalletId());
            history.setTradeNo(refundNo);
            history.setOutTradeNo(tradeHistory.getOutTradeNo());
            history.setOutRefundNo(form.getOutRefundNo());
            history.setBody(form.getReason());
            history.setSubject(form.getReason());
            history.setType(ConstantPay.Type.REFUND);
            history.setTotalAmount(form.getTotalAmount());
            history.setStatus(ConstantPay.Status.COMPLETE_SUCCESS);
            history.setExpireTime(LocalDateTime.now().plusYears(7));
            walletHistoryService.add(history);
            // 直接调用余额增加的业务
            walletService.increase(tradeHistory.getFkWalletId(), form.getTotalAmount());
            // 修改原订单信息
            tradeHistory.setStatus(refundAmount + form.getTotalAmount() < payAmount ? ConstantPay.Status.COMPLETE_REFUND_PART : ConstantPay.Status.COMPLETE_REFUND);
            walletHistoryService.update(tradeHistory);
            // 调用回调信息
            callback(walletPayProperty.getNotifyUrl(), history);
        });
    }

    /**
     * 订单支付操作
     *
     * @param tradeNo 订单号
     */
    @Transactional(rollbackFor = Exception.class)
    public void pay(String tradeNo) {
        // 处理订单信息（开启订单锁）
        orderLockUtil.lock(tradeNo, () -> {
            // 获取订单信息
            var order = walletHistoryService.getByTradeNo(tradeNo);
            // 验证订单信息
            if (order == null) {
                throw new WalletException("订单不存在");
            }
            if (PAY_ERROR_MESSAGE.containsKey(order.getStatus())) {
                throw new WalletException(PAY_ERROR_MESSAGE.get(order.getStatus()));
            }
            // 验证订单状态
            if (order.getStatus() != ConstantPay.Status.NEW_BUILT) {
                throw new WalletException("订单状态不正确");
            }
            // 判断是否是支付订单
            if (order.getType() != ConstantPay.Type.PAY) {
                throw new WalletException("订单类型不正确");
            }
            order.setStatus(ConstantPay.Status.COMPLETE_SUCCESS);
            order.setPayTime(LocalDateTime.now());
            // 支付成功后的操作信息
            var userId = order.getFkUserId();
            // 获取钱包信息
            var wallet = walletService.getByUserId(userId);
            // 验证订单信息
            walletLockUtil.lock(userId, () -> {
                // 如果是消费的话，验证余额是否大于等于订单金额
                if (wallet.getBalance() < order.getTotalAmount()) {
                    throw new WalletException("余额不足");
                }
                walletService.decrease(wallet.getId(), order.getTotalAmount());
                // 修改订单状态
                walletHistoryService.update(order);
                // 调用回调信息
                callback(walletPayProperty.getNotifyUrl(), order);
            });
        });
    }

    /**
     * 订单通知操作
     *
     * @param type        交易类型
     * @param outTradeNo  商户订单号
     * @param outRefundNo 商户退款订单号
     */
    public void notify(int type, String outTradeNo, String outRefundNo) {
        switch (type) {
            case ConstantPay.Type.INCOME ->
                    tradeSuccessHandlerServiceList.forEach(method -> method.income(PayEnum.Method.BALANCE, outTradeNo));
            case ConstantPay.Type.PAY ->
                    tradeSuccessHandlerServiceList.forEach(method -> method.pay(PayEnum.Method.BALANCE, outTradeNo));
            case ConstantPay.Type.REFUND -> {
                tradeSuccessHandlerServiceList.forEach(method -> method.refund(PayEnum.Method.BALANCE, outTradeNo, outRefundNo));
                // 验证订单信息
                billHistoryService.checkRefund(outTradeNo);
            }
        }
    }

    /**
     * 调用回调信息
     *
     * @param callback 回调地址
     * @param order    订单信息
     */
    private void callback(String callback, WalletHistory order) {
        executorUtil.execute(() -> {
                    if (StringUtils.isNotBlank(callback)) {
                        // 设置回调信息
                        var entity = new PayResultEntity(order);
                        if (callback.startsWith("http")) {
                            var map = new HashMap<String, String>() {{
                                put("content", JSON.toJSONString(entity));
                            }};
                            HttpRequest.post(callback).contentType("application/json").body(JSON.toJSONString(map)).execute();
                        }
                    } else {
                        notify(order.getType(), order.getOutTradeNo(), order.getOutRefundNo());
                    }
                }
        );
    }

}
